﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Threading;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;
using System.Diagnostics;
using System.Collections;
using System.Web;

/*    
    Copyright (C) 2010 Officebroker.com LTD.
    Neil Cadman, Alex Welding, Andrew Coombs, Jan Rygielski
    Sir Robert Peel Mill, Mill Lane, Staffordshire, Fazely, B78 3QD, England

    This file is part of OBiCMSFramework.

    OBiCMSFramework is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OBiCMSFramework is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License (GNU AGPL)
    along with OBiCMSFramework.  If not, see <http://www.gnu.org/licenses/>.
*/

namespace OBiCMSFramework
{
     [Serializable()]
    public class Template : BasePage.BasePageJob.BasePageJobType
    {
        // Private Variables
        private static Hashtable _cachedTemplatesHash = new Hashtable();
        private static List<Template> _cachedTemplates = new List<Template>();

        private Boolean _runThreadAgain = true;
        private DateTime _dateChanged;
        private Int32 _minRefreshTime = 60000;
        private Int32 _maxRefreshTime = 120000;
        private List<String> _informedBasePages = new List<String>();
        private List<BasePage.CSSSnippet> _cssSnippets = new List<BasePage.CSSSnippet>();
        private String _id;
        private String _body;
        private String _version = "0";
        private String _javascript = "";
        private String _javascriptDefered = "";

        // Public Properties
        internal DateTime DateChanged
        { get { return _dateChanged; } }
        internal static Hashtable CachedTemplatesHash
        {
            get { return _cachedTemplatesHash; }
        }
        internal static List<Template> CachedTemplates
        {
            get { return _cachedTemplates; }
        }
        internal String ID
        { get { return _id; } }
        internal String Version
        { get { return _version; } }

        // Private Delegates
        private delegate void loadTemplateDelegate(Int32 startStall);

        // Public Constructors
        private Template(String templateID)
        {
            _id = templateID;
        }

        // Pubic methods
        public static Template getTemplate(String templateID)
        {
            lock (_cachedTemplatesHash)
            {
                if (_cachedTemplatesHash.ContainsKey(templateID))
                    return (Template)_cachedTemplatesHash[templateID];

                if (File.Exists(StaticObjects.SavedStatesLocation + templateID + ".template"))
                {
                    StaticObjects.DeSerialize(StaticObjects.SavedStatesLocation + templateID + ".template");
                    return (Template)_cachedTemplatesHash[templateID];
                }

                return Template.GenerateNewTemplate(templateID);
            }
        }
        public StringBuilder GenerateTemplate(BasePage.ContextManager context, BasePage.PageSettings pageSettings, String xmlSettings, Stopwatch timer, Boolean useDiagnostics)
        {
            return GeneratePage(context, pageSettings, xmlSettings, timer, useDiagnostics);
        }

        // Private Methods
        private void AutoLoad()
        {
            loadTemplateDelegate autoLoad = new loadTemplateDelegate(loadTemplate);
            autoLoad.BeginInvoke(StaticObjects.RandomFactory.Create().Next(_minRefreshTime, _maxRefreshTime), new AsyncCallback(AutoLoadEnd), null);
        }
        private void loadTemplate(Int32 startStall)
        {
            DataRow dr;
            DataSet ds;
            String sql;

            if (startStall != 0)
                Thread.Sleep(startStall);

            // Load the Template
            sql = "select * from getTemplate('" + this.ID + "') Order by Version Desc";

            ds = StaticObjects.GetDataset(sql);

            if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
                throw new OBiHTTPHandler.CMSException("Template Cannot Load.", new Dictionary<String, String>() {
                                                                                 {"Template ID", this.ID} });

            dr = ds.Tables[0].Rows[0];

            // Has the version changed, if so reload the pages settings
            if (_version != dr["Version"].ToString())
            {
                List<BasePage.CSSSnippet> newCSSsnippets = new List<BasePage.CSSSnippet>();
                String[] javascriptTypes;

                javascriptTypes = Regex.Split(dr["Javascript"].ToString(), "#_#_#_#Defered#_#_#_#");

                _javascript = javascriptTypes[0];

                if (javascriptTypes.Length > 1)
                    _javascriptDefered = javascriptTypes[1];

                _body = dr["Body"].ToString();

                String cssSQL = "select * from getTemplatesCssSnippets('" + this.ID + "') Order By CMSCSSSnippetLoadOrder Asc";
                DataSet cssDS = StaticObjects.GetDataset(cssSQL);

                foreach (DataRow cssDR in cssDS.Tables[0].Rows)
                {
                    BasePage.CSSSnippet newSnippet = BasePage.GetCssSnippet(cssDR["CMSCssSnippetID"].ToString());
                    newCSSsnippets.Add(newSnippet);
                }

                _cssSnippets = newCSSsnippets;

                _version = dr["Version"].ToString();

                // Update the changes time to now.
                _dateChanged = DateTime.UtcNow;
                _informedBasePages.Clear();
            }

            // Automaticly run again?
            if (_runThreadAgain)
                AutoLoad();
        }
        private static Template GenerateNewTemplate(String templateID)
        {
            Template newTemplate = new Template(templateID);
            newTemplate.InitializeTemplate();
            _cachedTemplates.Add(newTemplate);
            _cachedTemplatesHash.Add(templateID, newTemplate);

            return newTemplate;
        }

        // Protected Methods
        protected void AutoLoadEnd(IAsyncResult result)
        {
        }

        // Internal Methods
        internal void StopAutoLoad()
        {
            _runThreadAgain = false;
        }
        internal void BeginAutoLoad()
        {
            _runThreadAgain = true;
            AutoLoad();
        }
        internal void InitializeTemplate()
        {
            _runThreadAgain = false;
            loadTemplate(0);
            _runThreadAgain = true;
            AutoLoad();
        }
        internal void AddToGlobalCSSList()
        {
            foreach (BasePage.CSSSnippet item in _cssSnippets)
            {
                if (!BasePage.CachedCssSnippetsHash.Contains(item.ID))
                {
                    item.BeginAutoLoad();
                    BasePage.CachedCssSnippets.Add(item);
                    BasePage.CachedCssSnippetsHash.Add(item.ID, item);
                }
            }
        }
        internal override StringBuilder GeneratePage(BasePage.ContextManager context, BasePage.PageSettings pageSettings, String xmlSettings, Stopwatch timer, Boolean useDiagnostics)
        {
            StringBuilder body = new StringBuilder();

            if (useDiagnostics)
                body.Append(Environment.NewLine + "<!-- Template ID : " + this.ID + Environment.NewLine +
                            "     Version : " + this.Version + Environment.NewLine +
                            "     Created at " + _dateChanged.ToString("yyyy/MM/dd HH:mm") + " yyyy/MM/dd HH:mm)" + Environment.NewLine +
                            "     Generated at " + timer.ElapsedTicks + "-->" + Environment.NewLine);

            if (!_informedBasePages.Contains(pageSettings.BasePageID))
            {
                pageSettings.HeaderChanges = true;
                _informedBasePages.Add(pageSettings.BasePageID);
            }

            body.Append(_body);

            pageSettings.AddCssSnippet(_cssSnippets);
            pageSettings.AppendJavaScript(_javascript);
            pageSettings.AppendJavaScriptDefered(_javascriptDefered);

            return body;
        }
    }
}
